﻿//-----------------------------------------------------------------------
// <copyright file="IMergeCellInternal.cs" company="LMT">
//     Copyright (c) by La Minh Thuận.
// </copyright>
// <author>thuanla1985@hotmail.com</author>
//-----------------------------------------------------------------------
namespace LMT.Forms
{
    using System.Drawing;
    using System.Windows.Forms;
    using LMT.Forms.Text;

    /// <summary>
    /// A CellHeader class has a merge ability
    /// </summary>
    public class MergeGridViewCellHeader : DataGridViewColumnHeaderCell 
    {
        /// <summary>
        /// StringFormat layout for drawing
        /// </summary>
        private StringFormat formatS;

        /// <summary>
        /// Rectanlge layout for drawing
        /// </summary>
        private RectangleF[] rectLayout;

        /// <summary>
        /// Brush background for cell header
        /// </summary>
        private Brush brushGr;

        /// <summary>
        /// Pen for drawing border
        /// </summary>
        private Pen border;

        /// <summary>
        /// Merge text for multi column header is spanning.
        /// </summary>
        private string mergeText;

        /// <summary>
        /// 
        /// </summary>
        private bool isDrawTopBorder;

        /// <summary>
        /// 
        /// </summary>
        private bool isDrawLeftBorder;

        /// <summary>
        /// 
        /// </summary>
        private static Font SortIconFont;

        /// <summary>
        /// Initializes a new instance of the <see cref="MergeGridViewCellHeader"/> class.
        /// </summary>
        public MergeGridViewCellHeader() : base()
        {
            this.SplitRow = 1;

            if (MergeGridViewCellHeader.SortIconFont == null)
            {
                MergeGridViewCellHeader.SortIconFont = new Font("Wingdings 3", 10);
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether this instance is spanning.
        /// </summary>
        /// <value>
        ///   <c>true</c> if this instance is spanning; otherwise, <c>false</c>.
        /// </value>
        internal bool[] IsSpanning { get; set; }

        /// <summary>
        /// Gets or sets the split row.
        /// </summary>
        /// <value>The split row.</value>
        internal short SplitRow
        { 
            get
            {
                return (short)this.rectLayout.Length;
            }

            set
            {
                this.rectLayout = new RectangleF[value];
                this.LeftOffSet = new ushort[value];
                this.RightOffSet = new ushort[value];
                this.IsSpanning = new bool[value];
                this.IsInitDraw = false;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether this instance is init draw.
        /// </summary>
        /// <value>
        ///   <c>true</c> if this instance is init draw; otherwise, <c>false</c>.
        /// </value>
        internal bool IsInitDraw { get; set; }

        /// <summary>
        /// Gets or sets the header span.
        /// </summary>
        /// <value>The header span.</value>
        internal ushort[] LeftOffSet { get; set; }

        /// <summary>
        /// Gets or sets the right off set.
        /// </summary>
        /// <value>The right off set.</value>
        internal ushort[] RightOffSet { get; set; }

        /// <summary>
        /// Return a MergeCellHeader object when editing on DataGridProperty
        /// </summary>
        /// <returns>
        /// An <see cref="T:System.Object"/> that represents the cloned <see cref="T:System.Windows.Forms.DataGridViewHeaderCell"/>.
        /// </returns>
        /// <PermissionSet>
        ///   <IPermission class="System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
        ///   <IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
        ///   <IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="UnmanagedCode, ControlEvidence"/>
        ///   <IPermission class="System.Diagnostics.PerformanceCounterPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
        /// </PermissionSet>
        public override object Clone()
        {
            MergeGridViewCellHeader merCol = (MergeGridViewCellHeader)base.Clone();
            merCol.IsSpanning = this.IsSpanning;
            merCol.LeftOffSet = this.LeftOffSet;
            merCol.RightOffSet = this.RightOffSet;
            merCol.rectLayout = this.rectLayout;
            
            return merCol;
        }

        /// <summary>
        /// Paints the specified graphics.
        /// </summary>
        /// <param name="graphics">The graphics.</param>
        /// <param name="clipBounds">The clip bounds.</param>
        /// <param name="cellBounds">The cell bounds.</param>
        /// <param name="rowIndex">Index of the row.</param>
        /// <param name="dataGridViewElementState">State of the data grid view element.</param>
        /// <param name="value">The value.</param>
        /// <param name="formattedValue">The formatted value.</param>
        /// <param name="errorText">The error text.</param>
        /// <param name="cellStyle">The cell style.</param>
        /// <param name="advancedBorderStyle">The advanced border style.</param>
        /// <param name="paintParts">The paint parts.</param>
        protected override void Paint(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, int rowIndex, DataGridViewElementStates dataGridViewElementState, object value, object formattedValue, string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts)
        {
            if (!this.IsInitDraw)
            {
                this.InitDrawing(cellBounds, value);
                this.IsInitDraw = true;
            }

            RectangleF merRect = this.GetMergeArea(cellBounds);
            graphics.FillRectangle(this.brushGr, cellBounds);

            // Draw header text
            this.FillText(graphics);

            // Draw the separator for header & first cell content
            graphics.DrawLine(this.border, merRect.Left, merRect.Bottom - 1, merRect.Right - 1, merRect.Bottom - 1);

            if (this.isDrawTopBorder)
            {
                graphics.DrawLine(this.border, merRect.Left, merRect.Top, merRect.Right - 1, merRect.Top);
            }

            if (cellBounds.Location.IsEmpty && this.isDrawLeftBorder)
            {
                graphics.DrawLine(this.border, merRect.Left, merRect.Top, merRect.Left, merRect.Bottom);
            }

            // Draw header border line
            for (int index = 0; index < this.rectLayout.Length; index++)
            {
                // The first line we can pass because it's same control border
                if (index > 0)
                {
                    graphics.DrawLine(this.border, merRect.Left, this.rectLayout[index].Top - 1, merRect.Right, this.rectLayout[index].Top - 1);
                }

                // Draw the right & left border if not merge and otherwise
                if (!this.IsSpanning[index] | (this.IsSpanning[index] && this.ColumnIndex == this.RightOffSet[index]))
                {
                    graphics.DrawLine(this.border, this.rectLayout[index].Left - 1, this.rectLayout[index].Top, this.rectLayout[index].Left - 1, this.rectLayout[index].Bottom);
                    graphics.DrawLine(this.border, this.rectLayout[index].Right - 1, this.rectLayout[index].Top, this.rectLayout[index].Right - 1, this.rectLayout[index].Bottom);

                    if (this.SortGlyphDirection != SortOrder.None)
                    {
                            graphics.DrawString(this.SortGlyphDirection == SortOrder.Ascending ? "p" : "q", 
                                                    MergeGridViewCellHeader.SortIconFont, Brushes.Black, 
                                                    this.rectLayout[index].Right - (MergeGridViewCellHeader.SortIconFont.Size * 2),
                                                    1);
                    }
                }
            }
        }

        /// <summary>
        /// Releases the unmanaged resources used by the <see cref="T:System.Windows.Forms.DataGridViewHeaderCell"/> and optionally releases the managed resources.
        /// </summary>
        /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
        protected override void Dispose(bool disposing)
        {
            // release managed resource
            if (this.brushGr != null)
            {
                this.brushGr.Dispose();
            }

            if (this.border != null)
            {
                this.border.Dispose();
            }

            if (this.formatS != null)
            {
                this.formatS.Dispose();
            }

            base.Dispose(disposing);
        }

        /// <summary>
        /// Inits the drawing.
        /// </summary>
        /// <param name="cellBound">The cell bound.</param>
        /// <param name="value">The value.</param>
        private void InitDrawing(RectangleF cellBound, object value)
        {
            this.formatS = Layouts.ParseAlignment(this.InheritedStyle.Alignment);
            if (this.IsSpanning[0])
            {
                this.mergeText = this.DataGridView.Columns[this.LeftOffSet[0]].HeaderText;
            }
            else
            {
                this.mergeText = value.ToString();
            }

            for (short row = 0; row < this.LeftOffSet.Length; row++)
            {
                if (this.IsSpanning[row])
                {
                    // Determine the total width of the merged cell
                    int width = 0;
                    for (int index = this.LeftOffSet[row]; index <= this.RightOffSet[row]; index++)
                    {
                        width += this.DataGridView.Columns[index].Width;
                    }

                    // Determine the width before the current cell.
                    int widthLeft = 0;
                    for (int index = this.LeftOffSet[row]; index < this.ColumnIndex; index++)
                    {
                        widthLeft += this.DataGridView.Columns[index].Width;
                    }

                    // Calculate area of the text
                    this.CalculateMergeSplitRect(row, cellBound, width, widthLeft);
                }
                else
                {
                    this.CalculateMergeSplitRect(row, cellBound, cellBound.Width, 0);
                }
            }

            Color headerBgClr = this.DataGridView.ColumnHeadersDefaultCellStyle.BackColor;
            this.brushGr = new SolidBrush(headerBgClr);
            this.border = new Pen(this.DataGridView.GridColor, 1.0F);

            MergeGridView mergeGrid = this.DataGridView as MergeGridView;
            if (mergeGrid != null)
            {
                this.isDrawTopBorder = mergeGrid.IsColumnTopBorder;
                this.isDrawLeftBorder = mergeGrid.IsColumnLeftBorder;
            }
        }

        /// <summary>
        /// Analyzes the merge split rect.
        /// </summary>
        /// <param name="rowIndex">Index of the row.</param>
        /// <param name="cellBound">The cell bound.</param>
        /// <param name="totalWidth">The total width of rectanlge.</param>
        /// <param name="totalWidthLeft">The total width from the left of control to the first of merge column.</param>
        private void CalculateMergeSplitRect(short rowIndex, RectangleF cellBound, float totalWidth, float totalWidthLeft)
        {
            float splitPart = cellBound.Height / this.rectLayout.Length;

            if (this.rectLayout.Length == 1)
            {
                this.rectLayout[0] = new RectangleF(cellBound.Left - totalWidthLeft, cellBound.Top, totalWidth, cellBound.Height);
            }
            else
            {
                this.rectLayout[rowIndex] = new RectangleF(cellBound.Left - totalWidthLeft, cellBound.Top + (splitPart * rowIndex), totalWidth, splitPart);
            }
        }

        /// <summary>
        /// Erases the background.
        /// </summary>
        /// <param name="cellBounds">The cell bounds.</param>
        /// <returns>RectangleF for whole merge area</returns>
        private RectangleF GetMergeArea(Rectangle cellBounds)
        {
            RectangleF merRect = this.rectLayout[0];
            foreach (RectangleF rect in this.rectLayout)
            {
                if (merRect.Width < rect.Width)
                {
                    merRect.Width = rect.Width;
                }
            }

            if (cellBounds.Height > merRect.Height)
            {
                merRect.Height = cellBounds.Height;
            }

            return merRect;
        }

        /// <summary>
        /// Fills the text.
        /// </summary>
        /// <param name="graphics">The graphics.</param>
        private void FillText(Graphics graphics)
        {
            RectangleF drawArea = RectangleF.Empty;
            // Header contains only row.
            if (this.LeftOffSet.Length == 1)
            {
                // Japanese char is large than Ansi char, so we increase the space between border and top of char.
                drawArea = this.rectLayout[0];
                drawArea.Offset(0.0F, 2.0F);

                graphics.DrawString(
                                    this.IsSpanning[0] ? this.mergeText : this.Value.ToString(),
                                    this.DataGridView.Font,
                                    Brushes.Black,
                                    drawArea,
                                    this.formatS);
            }
            else
            {
                string[] textMerge = this.mergeText.Split(',');
                string[] textOwner = this.Value.ToString().Split(',');

                for (short txtIndex = 0; txtIndex < this.rectLayout.Length; txtIndex++)
                {
                    if (this.IsSpanning[txtIndex])
                    {
                        if (txtIndex < textMerge.Length)
                        {
                            // Japanese char is large than Ansi char, so we increase the space between border and top of char.
                            drawArea = this.rectLayout[txtIndex];
                            drawArea.Offset(0.0F, 1.50F);

                            //graphics.FillRectangle(this.brushGr, this.rectLayout[txtIndex]);
                            graphics.DrawString(textMerge[txtIndex].Trim(), this.DataGridView.Font, Brushes.Black, drawArea, this.formatS);
                        }
                    }
                    else
                    {
                        if (txtIndex < textOwner.Length)
                        {
                            // Japanese char is large than Ansi char, so we increase the space between border and top of char.
                            drawArea = this.rectLayout[txtIndex];
                            drawArea.Offset(0.0F, 1.50F);

                            //graphics.FillRectangle(this.brushGr, this.rectLayout[txtIndex]);
                            graphics.DrawString(textOwner[txtIndex].Trim(), this.DataGridView.Font, Brushes.Black, drawArea, this.formatS);
                        }
                    }
                }
            }
        }
    }
}
